
#include "OGGStream.h"
#include "AudioCore.h"
#include "../Logger.h"
#include <string>
#include <cstdio>

#if defined(_WIN32)
#include <al.h>
#include <alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif

namespace al
{

OGGstream::OGGstream()
{
    buffers = new ALuint[2];
}

OGGstream::~OGGstream()
{
    Release( );
    delete[] buffers;
}

bool OGGstream::Open(const char* fname)
{
    if( !( oggFile = fopen( fname, "rb" ) ) )
    {
        //error
        return false;
    }

    if( ov_open_callbacks( oggFile, &oggStream, NULL, 0, OV_CALLBACKS_DEFAULT ) < 0 )
    {
        fclose( oggFile );
        //error
        return false;
    }

    /*
     * The function 'ov_open' binds the file handle with the Ogg stream.
     * The stream now 'owns' this file handle so don't go messing around with it yourself.*/

    vorbisInfo = ov_info( &oggStream, -1 );

    if( vorbisInfo->channels == 1 )
        format = AL_FORMAT_MONO16;
    else
        format = AL_FORMAT_STEREO16;

    /* This grabs some information on the file.
     * We extract the OpenAL format enumerator
     * based on how many channels are in the Ogg.*/

    alGenBuffers( 2, buffers );
    // AudioCore::GetInstance( )->Check( );

    return true;
}

void OGGstream::Release()
{
    alDeleteBuffers( 2, buffers );
    ov_clear( &oggStream );
}

void OGGstream::UnqueueBuffers(ALuint source)
{
    alSourceUnqueueBuffers( source, 2, buffers );
}

/**
 *
 */
bool OGGstream::InitializeStream(ALuint source)
{
    //initialize with data both buffers.
    if( Stream( buffers[0] ) == SUR_ERROR )
        return false;

    if( !Stream( buffers[1] ) == SUR_ERROR )
        return false;

    //and queue them to the source.
    alSourceQueueBuffers( source, 2, buffers );
    return true;
}

/**
 * Gets all processed buffers and refills them with data.
 */
STREAM_UPDATE_RESULT OGGstream::Update(ALuint source, bool loop)
{
    int processed;
    //get processed buffers
    alGetSourcei( source, AL_BUFFERS_PROCESSED, &processed );

    //for each buffer which has been processed 
    while( processed-- )
    {
        //get idle buffer from source
        ALuint buffer;
        alSourceUnqueueBuffers( source, 1, &buffer );
        //  AudioCore::GetInstance( )->Check( );
        //fill it up with data
        if( Stream( buffer ) == SUR_EOF )
        {
            //in case of a loop rewind the filestream 
            //and fill the buffer with data.
            if( loop )
            {
                Rewind( source );
                if( Stream( buffer ) == SUR_ERROR )
                {
                    return SUR_ERROR;
                }
                //and enqueue it back
                alSourceQueueBuffers( source, 1, &buffer );
                //notify the source about the loop
                return SUR_LOOP;
            }
            return SUR_EOF;
        }
        else
        {
            //and enqueue it back
            alSourceQueueBuffers( source, 1, &buffer );
            return SUR_OK;
        }

    }
    return SUR_ERROR;
}

void OGGstream::Rewind(ALuint source)
{
    //ckeck if stream supports seek
    if( ov_seekable( &oggStream ) )//may be removed
    {
        //and go to stream's start
        ov_raw_seek( &oggStream, 0 );
    }
    else
    {
        // LOG( Logger::CHANNEL_AUDIO, LogFileStream::LEVEL_WARNING ) << "OGGstream: failed to rewind non seekable stream.";
    }
}

STREAM_UPDATE_RESULT OGGstream::Stream(ALuint buffer)
{
    char data[BUFFER_SIZE]; //placeholder of audio data
    int size = 0; //the total data size that we read
    int section; //unused 
    int result;

    while( size < BUFFER_SIZE )//we may need multiple reads in order to fill the entire buffer
    {
        result = ov_read( &oggStream, data + size, BUFFER_SIZE - size, ENDIAN, WORD_SIZE, SGNED, &section );

        if( result > 0 )
        {
            size += result;
        }
        else if( result < 0 ) //an error occured 
        {
            return SUR_ERROR;
        }
        else //eof
        {
            return SUR_EOF;
        }
    }

    AudioCore::ClearErrorBuffer( );
    //add the read buffer to openal buffer
    alBufferData( buffer, format, data, size, vorbisInfo->rate );
    //make sure everything is ok
    if( !AudioCore::Check( ) )
    {
        return SUR_ERROR;
    }

    return SUR_OK;
}

}
